package l2hc.commons.util;

import org.apache.commons.math.random.MersenneTwister;
import org.apache.commons.math.random.RandomGenerator;

import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import l2hc.gameserver.utils.Location;

public class Rnd
{
	private Rnd()
	{
	}

	private static final ThreadLocal<RandomGenerator> rnd = new ThreadLocalGeneratorHolder();

	private static AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);

	static final class ThreadLocalGeneratorHolder extends ThreadLocal<RandomGenerator>
	{
		@Override
		public RandomGenerator initialValue()
		{
			return new MersenneTwister(seedUniquifier.getAndIncrement() + System.nanoTime());
		}
	}

	private static RandomGenerator rnd()
	{
		return rnd.get();
	}

	public static double get() // get random number from 0 to 1
	{
		return rnd().nextDouble();
	}

	/**
	 * Gets a random number from 0(inclusive) to n(exclusive)
	 * 
	 * @param n
	 *            The superior limit (exclusive)
	 * @return A number from 0 to n-1
	 */
	public static int get(int n)
	{
		return rnd().nextInt(n);
	}

	public static long get(long n)
	{
		return (long) (rnd().nextDouble() * n);
	}

	public static int get(int min, int max) // get random number from min to max
											// (not max-1 !)
	{
		return min + get(max - min + 1);
	}

	public static long get(long min, long max) // get random number from min to
												// max (not max-1 !)
	{
		return min + get(max - min + 1);
	}

	public static int nextInt()
	{
		return rnd().nextInt();
	}

	public static double nextDouble()
	{
		return rnd().nextDouble();
	}

	public static double nextGaussian()
	{
		return rnd().nextGaussian();
	}

	public static boolean nextBoolean()
	{
		return rnd().nextBoolean();
	}

	/**
	 * Рандомайзер для подсчета шансов.<br>
	 * Рекомендуется к использованию вместо Rnd.get()
	 * 
	 * @param chance
	 *            в процентах от 0 до 100
	 * @return true в случае успешного выпадания. <li>Если chance <= 0, вернет
	 *         false <li>Если chance >= 100, вернет true
	 */
	public static boolean chance(int chance)
	{
		return chance >= 1 && (chance > 99 || rnd().nextInt(99) + 1 <= chance);
	}

	/**
	 * Рандомайзер для подсчета шансов.<br>
	 * Рекомендуется к использованию вместо Rnd.get() если нужны очень маленькие
	 * шансы
	 * 
	 * @param chance
	 *            в процентах от 0 до 100
	 * @return true в случае успешного выпадания. <li>Если chance <= 0, вернет
	 *         false <li>Если chance >= 100, вернет true
	 */
	public static boolean chance(double chance)
	{
		return rnd().nextDouble() <= chance / 100.;
	}

	public static <E> E get(E[] list)
	{
		return list[get(list.length)];
	}

	public static int get(int[] list)
	{
		return list[get(list.length)];
	}

	public static <E> E get(List<E> list)
	{
		return list.get(get(list.size()));
	}
	
	public static Location coordsRandomize(int x, int y, int z, int heading, int radius_min, int radius_max)
	{
		if(radius_max == 0 || radius_max < radius_min)
		{
			return new Location(x, y, z, heading);
		}
		int radius = get(radius_min, radius_max);
		double angle = rnd().nextDouble() * 2 * Math.PI;
		return new Location((int) (x + radius * Math.cos(angle)), (int) (y + radius * Math.sin(angle)), z, heading);
	}

}